# -*- coding: utf-8 -*-
"""行为分析器模块"""

import logging
import numpy as np
import time
from collections import deque

logger = logging.getLogger(__name__)

class BehaviorAnalyzer:
    """行为分析器类，用于分析目标的行为模式"""
    
    def __init__(self):
        """初始化行为分析器"""
        self.config = {}
        self.is_initialized = False
        self.tracked_objects_history = {}
        self.zones = {}
        self.directions = {}
        self.last_analysis_time = time.time()
    
    def initialize(self, config):
        """初始化行为分析器
        
        Args:
            config: 行为分析器配置字典
            
        Returns:
            bool: 初始化是否成功
        """
        try:
            logger.info("初始化行为分析器...")
            self.config = config
            
            # 初始化区域定义
            self._initialize_zones(config.get('zones', {}))
            
            # 初始化方向定义
            self._initialize_directions(config.get('directions', {}))
            
            self.is_initialized = True
            logger.info("行为分析器初始化成功")
            return True
            
        except Exception as e:
            logger.error(f"初始化行为分析器失败: {str(e)}")
            return False
    
    def _initialize_zones(self, zones_config):
        """初始化区域定义
        
        Args:
            zones_config: 区域配置字典
        """
        self.zones = zones_config
        logger.info(f"已初始化 {len(zones_config)} 个分析区域")
    
    def _initialize_directions(self, directions_config):
        """初始化方向定义
        
        Args:
            directions_config: 方向配置字典
        """
        self.directions = directions_config
        logger.info(f"已初始化 {len(directions_config)} 个方向规则")
    
    def analyze(self, tracked_objects, frame_info=None):
        """分析目标行为
        
        Args:
            tracked_objects: 跟踪的目标对象列表
            frame_info: 帧信息字典
            
        Returns:
            list: 分析结果列表，每个元素包含违规信息
        """
        if not self.is_initialized:
            logger.error("行为分析器未初始化")
            return []
        
        try:
            current_time = time.time()
            violations = []
            
            # 更新目标历史记录
            self._update_objects_history(tracked_objects, current_time)
            
            # 分析每个目标
            for obj in tracked_objects:
                track_id = obj['track_id']
                class_id = obj['class_id']
                class_name = obj['class_name']
                bbox = obj['bbox']
                center = obj['center']
                direction = obj.get('direction', (0, 0))
                speed = obj.get('speed', 0)
                trajectory = obj.get('trajectory', [])
                tracking_time = obj.get('tracking_time', 0)
                
                # 获取目标历史信息
                obj_history = self.tracked_objects_history.get(track_id, {})
                previous_positions = obj_history.get('positions', [])
                zone_history = obj_history.get('zone_history', [])
                
                # 1. 区域入侵检测
                zone_violations = self._detect_zone_violations(track_id, class_id, class_name, 
                                                              center, bbox, zone_history)
                violations.extend(zone_violations)
                
                # 2. 方向违规检测
                direction_violations = self._detect_direction_violations(track_id, class_id, class_name,
                                                                       center, direction, trajectory)
                violations.extend(direction_violations)
                
                # 3. 速度违规检测
                speed_violations = self._detect_speed_violations(track_id, class_id, class_name,
                                                                center, speed)
                violations.extend(speed_violations)
                
                # 4. 停车违规检测
                parking_violations = self._detect_parking_violations(track_id, class_id, class_name,
                                                                    center, previous_positions, 
                                                                    tracking_time)
                violations.extend(parking_violations)
                
                # 5. 逆行检测
                wrong_way_violations = self._detect_wrong_way(track_id, class_id, class_name,
                                                             center, trajectory)
                violations.extend(wrong_way_violations)
                
                # 6. 徘徊检测
                loitering_violations = self._detect_loitering(track_id, class_id, class_name,
                                                            center, previous_positions, 
                                                            tracking_time)
                violations.extend(loitering_violations)
            
            # 定期清理过期数据
            if current_time - self.last_analysis_time > 5.0:  # 每5秒清理一次
                self._cleanup_old_history(current_time)
                self.last_analysis_time = current_time
            
            return violations
            
        except Exception as e:
            logger.error(f"行为分析失败: {str(e)}")
            return []
    
    def _update_objects_history(self, tracked_objects, current_time):
        """更新目标历史记录
        
        Args:
            tracked_objects: 跟踪的目标对象列表
            current_time: 当前时间
        """
        for obj in tracked_objects:
            track_id = obj['track_id']
            center = obj['center']
            bbox = obj['bbox']
            
            if track_id not in self.tracked_objects_history:
                self.tracked_objects_history[track_id] = {
                    'positions': deque(maxlen=100),  # 保存最近100个位置
                    'times': deque(maxlen=100),      # 对应的时间戳
                    'zone_history': [],              # 区域历史
                    'last_violation': {},            # 最近的违规记录
                    'first_seen': current_time       # 首次出现时间
                }
            
            history = self.tracked_objects_history[track_id]
            history['positions'].append(center)
            history['times'].append(current_time)
            
            # 更新区域历史
            current_zones = self._get_object_zones(center, bbox)
            for zone_id, zone_info in current_zones.items():
                # 检查是否需要添加新的区域记录
                if not zone_history or zone_history[-1]['zone_id'] != zone_id:
                    history['zone_history'].append({
                        'zone_id': zone_id,
                        'enter_time': current_time
                    })
                else:
                    # 更新区域停留时间
                    history['zone_history'][-1]['last_seen'] = current_time
    
    def _get_object_zones(self, center, bbox):
        """获取目标所在的区域
        
        Args:
            center: 目标中心点
            bbox: 目标边界框
            
        Returns:
            dict: 区域ID到区域信息的映射
        """
        result = {}
        
        for zone_id, zone_config in self.zones.items():
            zone_type = zone_config.get('type', 'polygon')
            
            if zone_type == 'polygon':
                points = zone_config.get('points', [])
                if points and self._point_in_polygon(center, points):
                    result[zone_id] = zone_config
            
            elif zone_type == 'rectangle':
                x1 = zone_config.get('x1', 0)
                y1 = zone_config.get('y1', 0)
                x2 = zone_config.get('x2', 0)
                y2 = zone_config.get('y2', 0)
                if x1 <= center[0] <= x2 and y1 <= center[1] <= y2:
                    result[zone_id] = zone_config
            
            elif zone_type == 'line':
                # 线需要特殊处理，通常用于跨线检测
                pass
        
        return result
    
    def _point_in_polygon(self, point, polygon):
        """判断点是否在多边形内部
        
        Args:
            point: (x, y) 点坐标
            polygon: 多边形顶点列表 [(x1, y1), (x2, y2), ...]
            
        Returns:
            bool: 是否在多边形内部
        """
        x, y = point
        n = len(polygon)
        inside = False
        
        p1x, p1y = polygon[0]
        for i in range(n + 1):
            p2x, p2y = polygon[i % n]
            if y > min(p1y, p2y):
                if y <= max(p1y, p2y):
                    if x <= max(p1x, p2x):
                        if p1y != p2y:
                            xints = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
                        if p1x == p2x or x <= xints:
                            inside = not inside
            p1x, p1y = p2x, p2y
        
        return inside
    
    def _detect_zone_violations(self, track_id, class_id, class_name, center, bbox, zone_history):
        """检测区域违规
        
        Args:
            track_id: 跟踪ID
            class_id: 类别ID
            class_name: 类别名称
            center: 中心点
            bbox: 边界框
            zone_history: 区域历史
            
        Returns:
            list: 违规列表
        """
        violations = []
        current_zones = self._get_object_zones(center, bbox)
        
        for zone_id, zone_config in current_zones.items():
            # 检查是否允许该类别的目标进入此区域
            allowed_classes = zone_config.get('allowed_classes', [])
            prohibited_classes = zone_config.get('prohibited_classes', [])
            
            if prohibited_classes and class_id in prohibited_classes:
                # 计算违规级别和描述
                severity = zone_config.get('severity', 'medium')
                violation_type = 'zone_violation'
                description = f"{class_name} (ID:{track_id}) 进入了禁止区域: {zone_config.get('name', zone_id)}"
                
                violations.append({
                    'track_id': track_id,
                    'class_id': class_id,
                    'class_name': class_name,
                    'type': violation_type,
                    'zone_id': zone_id,
                    'zone_name': zone_config.get('name', zone_id),
                    'severity': severity,
                    'description': description,
                    'position': center,
                    'timestamp': time.time(),
                    'bbox': bbox
                })
            
            # 检查区域停留时间
            max_stay_time = zone_config.get('max_stay_time', 0)
            if max_stay_time > 0 and zone_history:
                for zone_record in zone_history:
                    if zone_record['zone_id'] == zone_id:
                        enter_time = zone_record.get('enter_time', 0)
                        stay_time = time.time() - enter_time
                        if stay_time > max_stay_time:
                            severity = zone_config.get('stay_time_severity', 'low')
                            violation_type = 'zone_stay_time_violation'
                            description = f"{class_name} (ID:{track_id}) 在区域 {zone_config.get('name', zone_id)} 停留超过 {max_stay_time} 秒"
                            
                            violations.append({
                                'track_id': track_id,
                                'class_id': class_id,
                                'class_name': class_name,
                                'type': violation_type,
                                'zone_id': zone_id,
                                'zone_name': zone_config.get('name', zone_id),
                                'severity': severity,
                                'description': description,
                                'position': center,
                                'timestamp': time.time(),
                                'bbox': bbox,
                                'stay_time': stay_time
                            })
                            break
        
        return violations
    
    def _detect_direction_violations(self, track_id, class_id, class_name, center, direction, trajectory):
        """检测方向违规
        
        Args:
            track_id: 跟踪ID
            class_id: 类别ID
            class_name: 类别名称
            center: 中心点
            direction: 移动方向
            trajectory: 轨迹
            
        Returns:
            list: 违规列表
        """
        violations = []
        
        # 检查是否有足够的轨迹点进行方向判断
        if len(trajectory) < 5:
            return violations
        
        # 计算实际移动方向（使用最近5个点的平均方向）
        recent_directions = []
        for i in range(1, len(trajectory)):
            dx = trajectory[i][0] - trajectory[i-1][0]
            dy = trajectory[i][1] - trajectory[i-1][1]
            if dx != 0 or dy != 0:
                recent_directions.append((dx, dy))
        
        if recent_directions:
            avg_dx = sum(d[0] for d in recent_directions) / len(recent_directions)
            avg_dy = sum(d[1] for d in recent_directions) / len(recent_directions)
            actual_direction = (avg_dx, avg_dy)
            
            # 检查每个方向规则
            for direction_id, direction_config in self.directions.items():
                # 检查是否适用于此类别的目标
                applicable_classes = direction_config.get('classes', [])
                if applicable_classes and class_id not in applicable_classes:
                    continue
                
                # 获取允许的方向范围
                allowed_angles = direction_config.get('allowed_angles', [])
                prohibited_angles = direction_config.get('prohibited_angles', [])
                
                # 计算实际方向的角度（度）
                angle_rad = np.arctan2(avg_dy, avg_dx)
                angle_deg = np.degrees(angle_rad) % 360
                
                # 检查是否违规
                if prohibited_angles:
                    for (start, end) in prohibited_angles:
                        if self._angle_in_range(angle_deg, start, end):
                            severity = direction_config.get('severity', 'medium')
                            violation_type = 'direction_violation'
                            description = f"{class_name} (ID:{track_id}) 违反了方向规则: {direction_config.get('name', direction_id)}"
                            
                            violations.append({
                                'track_id': track_id,
                                'class_id': class_id,
                                'class_name': class_name,
                                'type': violation_type,
                                'direction_id': direction_id,
                                'direction_name': direction_config.get('name', direction_id),
                                'severity': severity,
                                'description': description,
                                'position': center,
                                'timestamp': time.time(),
                                'actual_angle': angle_deg,
                                'prohibited_range': (start, end)
                            })
                            break
        
        return violations
    
    def _angle_in_range(self, angle, start, end):
        """判断角度是否在指定范围内
        
        Args:
            angle: 要检查的角度（度）
            start: 范围起始角度（度）
            end: 范围结束角度（度）
            
        Returns:
            bool: 是否在范围内
        """
        # 规范化角度
        angle = angle % 360
        start = start % 360
        end = end % 360
        
        if start <= end:
            return start <= angle <= end
        else:
            return angle >= start or angle <= end
    
    def _detect_speed_violations(self, track_id, class_id, class_name, center, speed):
        """检测速度违规
        
        Args:
            track_id: 跟踪ID
            class_id: 类别ID
            class_name: 类别名称
            center: 中心点
            speed: 速度
            
        Returns:
            list: 违规列表
        """
        violations = []
        
        # 获取速度限制配置
        speed_limits = self.config.get('speed_limits', {})
        class_speed_limit = speed_limits.get(str(class_id), speed_limits.get('default', 0))
        
        if class_speed_limit > 0 and speed > class_speed_limit:
            # 计算超速百分比
            over_speed_pct = ((speed - class_speed_limit) / class_speed_limit) * 100
            
            # 确定违规级别
            if over_speed_pct >= 50:
                severity = 'high'
            elif over_speed_pct >= 20:
                severity = 'medium'
            else:
                severity = 'low'
            
            violation_type = 'speed_violation'
            description = f"{class_name} (ID:{track_id}) 超速: {speed:.1f} > {class_speed_limit} (超出 {over_speed_pct:.1f}%)"
            
            violations.append({
                'track_id': track_id,
                'class_id': class_id,
                'class_name': class_name,
                'type': violation_type,
                'severity': severity,
                'description': description,
                'position': center,
                'timestamp': time.time(),
                'actual_speed': speed,
                'limit_speed': class_speed_limit,
                'over_speed_pct': over_speed_pct
            })
        
        return violations
    
    def _detect_parking_violations(self, track_id, class_id, class_name, center, previous_positions, tracking_time):
        """检测停车违规
        
        Args:
            track_id: 跟踪ID
            class_id: 类别ID
            class_name: 类别名称
            center: 中心点
            previous_positions: 历史位置
            tracking_time: 跟踪时间
            
        Returns:
            list: 违规列表
        """
        violations = []
        
        # 只有车辆类别的目标才需要检查停车
        if class_id not in [2, 3, 5, 7]:  # car, motorbike, bus, truck
            return violations
        
        # 获取停车检测配置
        parking_config = self.config.get('parking_detection', {})
        enabled = parking_config.get('enabled', True)
        min_still_time = parking_config.get('min_still_time', 30)  # 最小静止时间（秒）
        max_movement_distance = parking_config.get('max_movement_distance', 20)  # 最大移动距离（像素）
        
        if not enabled or tracking_time < min_still_time:
            return violations
        
        # 检查是否静止
        if len(previous_positions) >= 5:
            # 计算最近5个位置的最大距离
            max_distance = 0
            recent_positions = previous_positions[-5:]
            
            for i in range(len(recent_positions)):
                for j in range(i+1, len(recent_positions)):
                    dx = recent_positions[i][0] - recent_positions[j][0]
                    dy = recent_positions[i][1] - recent_positions[j][1]
                    distance = np.sqrt(dx*dx + dy*dy)
                    max_distance = max(max_distance, distance)
            
            # 检查是否在停车禁止区域
            in_parking_prohibited_zone = False
            for zone_id, zone_config in self.zones.items():
                if zone_config.get('type') == 'parking_prohibited' and self._point_in_polygon(center, zone_config.get('points', [])):
                    in_parking_prohibited_zone = True
                    zone_name = zone_config.get('name', zone_id)
                    break
            
            # 判断是否停车违规
            if in_parking_prohibited_zone and max_distance < max_movement_distance:
                severity = parking_config.get('severity', 'medium')
                violation_type = 'parking_violation'
                description = f"{class_name} (ID:{track_id}) 在禁止停车区域停车: {zone_name}"
                
                violations.append({
                    'track_id': track_id,
                    'class_id': class_id,
                    'class_name': class_name,
                    'type': violation_type,
                    'severity': severity,
                    'description': description,
                    'position': center,
                    'timestamp': time.time(),
                    'parking_time': tracking_time,
                    'zone_name': zone_name
                })
        
        return violations
    
    def _detect_wrong_way(self, track_id, class_id, class_name, center, trajectory):
        """检测逆行
        
        Args:
            track_id: 跟踪ID
            class_id: 类别ID
            class_name: 类别名称
            center: 中心点
            trajectory: 轨迹
            
        Returns:
            list: 违规列表
        """
        violations = []
        
        # 只有车辆类别的目标才需要检查逆行
        if class_id not in [2, 3, 5, 7]:  # car, motorbike, bus, truck
            return violations
        
        # 获取逆行检测配置
        wrong_way_config = self.config.get('wrong_way_detection', {})
        enabled = wrong_way_config.get('enabled', True)
        
        if not enabled:
            return violations
        
        # 检查是否有足够的轨迹点进行逆行判断
        if len(trajectory) < 10:
            return violations
        
        # 检查每个方向规则中的逆行定义
        for direction_id, direction_config in self.directions.items():
            if direction_config.get('detect_wrong_way', False):
                # 检查目标是否在此方向规则的区域内
                region_points = direction_config.get('region', [])
                if region_points and self._point_in_polygon(center, region_points):
                    # 计算移动方向的主要分量
                    # 使用最近10个点计算总体移动趋势
                    start_point = trajectory[0]
                    end_point = trajectory[-1]
                    dx = end_point[0] - start_point[0]
                    dy = end_point[1] - start_point[1]
                    
                    # 获取该方向规则定义的正确方向
                    correct_direction = direction_config.get('correct_direction', (0, 0))
                    
                    # 计算夹角余弦值，判断方向是否相反
                    if dx != 0 or dy != 0:
                        # 归一化向量
                        actual_norm = np.sqrt(dx*dx + dy*dy)
                        correct_norm = np.sqrt(correct_direction[0]**2 + correct_direction[1]** 2)
                        
                        if actual_norm > 0 and correct_norm > 0:
                            # 计算点积
                            dot_product = (dx * correct_direction[0] + dy * correct_direction[1]) / (actual_norm * correct_norm)
                            
                            # 如果夹角大于90度，则认为是逆行
                            if dot_product < 0:
                                severity = wrong_way_config.get('severity', 'high')
                                violation_type = 'wrong_way_violation'
                                description = f"{class_name} (ID:{track_id}) 逆行: {direction_config.get('name', direction_id)}"
                                
                                violations.append({
                                    'track_id': track_id,
                                    'class_id': class_id,
                                    'class_name': class_name,
                                    'type': violation_type,
                                    'direction_id': direction_id,
                                    'direction_name': direction_config.get('name', direction_id),
                                    'severity': severity,
                                    'description': description,
                                    'position': center,
                                    'timestamp': time.time(),
                                    'dot_product': dot_product
                                })
        
        return violations
    
    def _detect_loitering(self, track_id, class_id, class_name, center, previous_positions, tracking_time):
        """检测徘徊
        
        Args:
            track_id: 跟踪ID
            class_id: 类别ID
            class_name: 类别名称
            center: 中心点
            previous_positions: 历史位置
            tracking_time: 跟踪时间
            
        Returns:
            list: 违规列表
        """
        violations = []
        
        # 只有人员类别需要检查徘徊
        if class_id != 0:  # person
            return violations
        
        # 获取徘徊检测配置
        loitering_config = self.config.get('loitering_detection', {})
        enabled = loitering_config.get('enabled', True)
        min_loitering_time = loitering_config.get('min_loitering_time', 60)  # 最小徘徊时间（秒）
        max_loitering_area = loitering_config.get('max_loitering_area', 5000)  # 最大徘徊区域面积（像素平方）
        
        if not enabled or tracking_time < min_loitering_time:
            return violations
        
        # 计算活动区域
        if len(previous_positions) >= 10:
            # 获取所有历史位置的边界
            x_coords = [p[0] for p in previous_positions]
            y_coords = [p[1] for p in previous_positions]
            min_x, max_x = min(x_coords), max(x_coords)
            min_y, max_y = min(y_coords), max(y_coords)
            
            # 计算活动区域
            activity_area = (max_x - min_x) * (max_y - min_y)
            
            # 检查是否在徘徊监控区域
            in_loitering_zone = False
            for zone_id, zone_config in self.zones.items():
                if zone_config.get('type') == 'loitering_monitor' and self._point_in_polygon(center, zone_config.get('points', [])):
                    in_loitering_zone = True
                    zone_name = zone_config.get('name', zone_id)
                    break
            
            # 判断是否徘徊
            if in_loitering_zone and activity_area < max_loitering_area:
                severity = loitering_config.get('severity', 'medium')
                violation_type = 'loitering_violation'
                description = f"{class_name} (ID:{track_id}) 在监控区域徘徊: {zone_name}"
                
                violations.append({
                    'track_id': track_id,
                    'class_id': class_id,
                    'class_name': class_name,
                    'type': violation_type,
                    'severity': severity,
                    'description': description,
                    'position': center,
                    'timestamp': time.time(),
                    'loitering_time': tracking_time,
                    'activity_area': activity_area,
                    'zone_name': zone_name
                })
        
        return violations
    
    def _cleanup_old_history(self, current_time):
        """清理过期的历史记录
        
        Args:
            current_time: 当前时间
        """
        max_history_time = self.config.get('max_history_time', 300)  # 5分钟
        expired_ids = []
        
        for track_id, history in self.tracked_objects_history.items():
            if history['times']:
                last_time = history['times'][-1]
                if current_time - last_time > max_history_time:
                    expired_ids.append(track_id)
        
        for track_id in expired_ids:
            del self.tracked_objects_history[track_id]
        
        if expired_ids:
            logger.debug(f"清理了 {len(expired_ids)} 个过期的目标历史记录")
    
    def get_analysis_stats(self):
        """获取分析统计信息
        
        Returns:
            dict: 统计信息
        """
        stats = {
            'tracked_objects': len(self.tracked_objects_history),
            'active_zones': len(self.zones),
            'active_directions': len(self.directions),
            'total_history_records': sum(len(h['positions']) for h in self.tracked_objects_history.values())
        }
        return stats
    
    def shutdown(self):
        """关闭行为分析器"""
        try:
            logger.info("关闭行为分析器...")
            self.tracked_objects_history.clear()
            self.zones.clear()
            self.directions.clear()
            self.is_initialized = False
            logger.info("行为分析器已关闭")
            
        except Exception as e:
            logger.error(f"关闭行为分析器时发生错误: {str(e)}")
    
    def is_ready(self):
        """检查分析器是否准备就绪
        
        Returns:
            bool: 是否就绪
        """
        return self.is_initialized

# 创建全局行为分析器实例
global_behavior_analyzer = None

def get_behavior_analyzer():
    """获取全局行为分析器实例
    
    Returns:
        BehaviorAnalyzer: 行为分析器实例
    """
    global global_behavior_analyzer
    if global_behavior_analyzer is None:
        global_behavior_analyzer = BehaviorAnalyzer()
    return global_behavior_analyzer